﻿using System;
using System.ServiceModel;
using System.Xml;
using GeneratedCode;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

namespace CRM2011PluginUtilities
{
    public sealed class GenerateFullNamePlugin : IPlugin
    {
        private static string firstNameAttributeName = string.Empty;
        private static string middleNameAttributeName = string.Empty;
        private static string lastNameAttributeName = string.Empty;
        private static string fullNameAttributeName = string.Empty;

        /// <summary>
        /// Override constructor used if the name attribute names are not the default ones. This requires adding XML to step configuration
        /// </summary>
        /// <param name="unsecureConfig"></param>
        /// <param name="secureConfig"></param>
        public GenerateFullNamePlugin(string unsecureConfig, string secureConfig)
        {
            if (string.IsNullOrEmpty(unsecureConfig))
            {
                throw new InvalidPluginExecutionException("Plugin step must have configuration XML");
            }
            ParseConfiguration(unsecureConfig);
        }

        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
            ITracingService tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            try
            {
                // Retrieve the organization setting for formatting full names (FullNameConventionCode)
                FullNameGenerator.FullNameConventionCode fullNameconventionCode = RetrieveFullNameConventionCode(context, service, tracer);

                Entity postEntity = (Entity)context.InputParameters["Target"];

                // Retrieve the value of the name attributes being created/updated
                string firstName = postEntity.Contains(firstNameAttributeName) ? postEntity[firstNameAttributeName] as string : null;
                string middleName = postEntity.Contains(middleNameAttributeName) ? postEntity[middleNameAttributeName] as string : null;
                string lastName = postEntity.Contains(lastNameAttributeName) ? postEntity[lastNameAttributeName] as string : null;

                if (string.Compare(context.MessageName, "Create", StringComparison.InvariantCultureIgnoreCase) != 0)
                {
                    // update / merge
                    tracer.Trace("Entity already existed and is being updated. Looking for old name values in the pre-image");
                    Entity preEntity = (Entity)context.PreEntityImages["PreImage"];

                    if (firstName == null && preEntity.Contains(firstNameAttributeName))
                    {
                        // Entity already had a firstname and is not getting updated
                        firstName = preEntity[firstNameAttributeName] as string;
                    }

                    if (middleName == null && preEntity.Contains(middleNameAttributeName))
                    {
                        // Entity already had a middleName and is not getting updated
                        middleName = preEntity[middleNameAttributeName] as string;
                    }

                    if (lastName == null && preEntity.Contains(lastNameAttributeName))
                    {
                        // Entity already had a lastName and is not getting updated
                        lastName = preEntity[lastNameAttributeName] as string;
                    }
                }

                tracer.Trace("Generating fullname from other name attributes");
                string fullName = FullNameGenerator.GenerateFullName(fullNameconventionCode, firstName, middleName, lastName);
                postEntity[fullNameAttributeName] = fullName;
            }
            catch (FaultException<OrganizationServiceFault> ex)
            {
                throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
            }
        }

        private FullNameGenerator.FullNameConventionCode RetrieveFullNameConventionCode(IPluginExecutionContext context, IOrganizationService service, ITracingService tracer)
        {
            tracer.Trace("Retrieving organization fullnameconventioncode");
            Organization organization = (Organization)service.Retrieve(Organization.EntityLogicalName, context.OrganizationId, new ColumnSet("fullnameconventioncode"));
            return (FullNameGenerator.FullNameConventionCode)organization.FullNameConventionCode.Value;
        }

        private void ParseConfiguration(string configuration)
        {
            XmlDocument xDoc = new XmlDocument();
            try
            {
                xDoc.LoadXml(configuration);
            }
            catch (Exception e)
            {
                throw new InvalidPluginExecutionException("Error while parsing the step configuration XML for the plugin", e);
            }
            XmlNode firstNameNode = xDoc.SelectSingleNode("./Configuration/FirstNameAttributeName");
            XmlNode middleNameNode = xDoc.SelectSingleNode("./Configuration/MiddleNameAttributeName");
            XmlNode lastNameNode = xDoc.SelectSingleNode("./Configuration/LastNameAttributeName");
            XmlNode fullNameNode = xDoc.SelectSingleNode("./Configuration/FullNameAttributeName");
            if (firstNameNode == null || middleNameNode == null || lastNameNode == null || fullNameNode == null)
            {
                throw new InvalidPluginExecutionException("Error in the configuration XML format");
            }
            firstNameAttributeName = firstNameNode.InnerText;
            middleNameAttributeName = middleNameNode.InnerText;
            lastNameAttributeName = lastNameNode.InnerText;
            fullNameAttributeName = fullNameNode.InnerText;
        }
    }
}
